Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consoles: Allow configuring VNC #1973

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

mvollmer
Copy link
Member

@mvollmer mvollmer commented Jan 14, 2025

Demo: https://youtu.be/02Gbc8OCA_M

Via the "Consoles" card, when the machine is off, but also via the VNC console tab when the machine is running and has no VNC.

This is the minimum to get #1795 finished, IMO, and we should not try to do more. Anything on top happens in #1972 and elsewhere, at its own pace.

  • only allow configuring the port, we are not comfortable with people opening the listening address without also adding encryption.
  • better UI for "auto" vs "manual" port.
  • talk about VNC in the not-running page instead of just "supported"
  • always mention port, either number or "automatic"
  • allow adding VNC while machine is running, properly warn about "pending changes", etc.
  • demo
  • more precise "shutdown required" detection
  • put a "shutdown required" message into the dialogs.
  • remove errors from dialog when fields are changed
  • tests, coverage

@garrett
Copy link
Member

garrett commented Jan 15, 2025

Can you add a screenshot of the current status please? Thanks!

@mvollmer
Copy link
Member Author

mvollmer commented Jan 15, 2025

Can you add a screenshot of the current status please? Thanks!

Yes! Thanks for having a look @garrett!

A runing virtual machine looks and behaves exactly like before. The console card of a not running machine has details about VNC.

Machine without VNC:

image

Machine with default VNC:

image

Machine with custom VNC:

image

Dialog for editing the VNC config:

image

Dialog for adding VNC:

image

(None of the above is meant to be final, of course.)

@garrett
Copy link
Member

garrett commented Jan 15, 2025

Thanks for adding the screenshots! I like what I'm seeing so far.

(None of the above is meant to be final, of course.)

It looks like a great start though, and I think it can be banged into resembling the mockups for this. Thanks again for picking up the work on Machines!

@mvollmer
Copy link
Member Author

I think it can be banged into resembling the mockups for this.

I think I didn't see any mockups of a stopped machine, hmm. Is this mockup also meant for stopped machines?

image

@mvollmer mvollmer force-pushed the console-vnc-edit branch 3 times, most recently from f5583f6 to 78c6280 Compare January 16, 2025 15:07
@mvollmer
Copy link
Member Author

A runing virtual machine looks and behaves exactly like before.

Sorry, that isn't true. A running machine without VNC now allows the selection of the VNC tab, which will look like this:

image

@mvollmer
Copy link
Member Author

I have changed the information display a bit. It deemphasizes the "VNC" term and the listening address.

No VNC:

image

VNC with auto port:

image

VNC with custom port:

image

In the selector, "VNC console" is changed to "Graphical console (VNC)".

image

The idea is that Cockpit talks about support for a graphical console inside Cockpit. That happens to be implemented via VNC, but that's a detail. And the per-VM listening address is not that interesting. People will very likely leave it at its default, and are better off changing the global default in qemu.conf in any case. The port on the other hand is interesting. VMs might fail to start if they use the same port by accident, and it is a genuine per-VM config setting.

@mvollmer
Copy link
Member Author

I have polished the dialogs some. They look like this now when everything is at default:

image

With a bogus port:

image

With a number but out of range:

image

@mvollmer mvollmer removed the no-test label Jan 17, 2025
@mvollmer mvollmer force-pushed the console-vnc-edit branch 5 times, most recently from aa46133 to 9757a3a Compare January 23, 2025 15:04
@mvollmer
Copy link
Member Author

Next round of screenshots!

Shut-off machine without VNC:

image

"Add VNC server" dialog:

image

With a static port and a failed input validation:

image

Running machine with a serial console but no VNC yet:

image

Running machine with freshly added VNC and in need of a shutdown:

image

src/components/vm/consoles/consoles.jsx Fixed Show fixed Hide fixed
src/components/vm/consoles/consoles.jsx Fixed Show fixed Hide fixed
src/components/vm/consoles/consoles.jsx Fixed Show fixed Hide fixed
src/components/vm/consoles/consoles.jsx Fixed Show fixed Hide fixed
src/components/vm/consoles/vncBody.jsx Fixed Show fixed Hide fixed
src/components/vm/consoles/vncBody.jsx Fixed Show fixed Hide fixed
@mvollmer mvollmer mentioned this pull request Jan 30, 2025
2 tasks
@mvollmer mvollmer force-pushed the console-vnc-edit branch 3 times, most recently from 8d8def3 to 07dd919 Compare January 31, 2025 10:34
- The dialogs talk about "server" and "listening" to make that
  clearer.

- We use the empty string instead of "-1" to signify automatic port
  assignment in the UI.

- The port does validation of its value.

- The password field takes the whole row and has can reveal its value.

- The dialogs warn if a shutdown is needed.

- The components take the whole "vm" object instead of separate name,
  id, and connectionName.
We are not comfortable with that without also offering encryption.
@mvollmer mvollmer force-pushed the console-vnc-edit branch 3 times, most recently from 9ea32bc to ac20d90 Compare January 31, 2025 12:04
- The "Console" card is always present and let's people manage VNC
  server settings while the machine is off.

- The "VNC console" tab is always present when the machine is running
  and let's people add VNC.

- There is no way yet to change VNC server settings for a running
  machine since there is no good place for the button that would open
  the dialog.
@mvollmer mvollmer marked this pull request as ready for review January 31, 2025 13:54
if (!active_vnc)
return true;
if (inactive_vnc.port != -1 && active_vnc.port != inactive_vnc.port)
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

if (inactive_vnc.port != -1 && active_vnc.port != inactive_vnc.port)
return true;
if (active_vnc.password != inactive_vnc.password)
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

.catch(ex => onAddErrorNotification({
text: cockpit.format(_("Failed to send key Ctrl+Alt+$0 to VM $1"), keyName, vmName),
text: cockpit.format(_("Failed to send key Ctrl+Alt+$0 to VM $1"), keyName, vm.name),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

detail: ex.message,
resourceId: vmId,
resourceId: vm.id,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

const { idPrefix, vm } = this.props;

const defaultBody = (
<Form onSubmit={e => e.preventDefault()} isHorizontal>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

dialogError: undefined,
saveDisabled: false,
vncCustomPort: Number(props.consoleDetail.port) != -1,
vncPort: Number(props.consoleDetail.port) == -1 ? "" : props.consoleDetail.port || "",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

const { idPrefix, vm } = this.props;

const defaultBody = (
<Form onSubmit={e => e.preventDefault()} isHorizontal>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

Copy link
Member

@garrett garrett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work on this, and we'll probably need this, but I don't think it's ready for merging.

It's not what we talked about, and we haven't heard back yet about the questions I asked (which were the questions we wondered about in person).

We need to talk about it and decide exactly how to move forward, which means we need a plan we can agree on, a design that we can target... and I can make some mockups on how it should look and act. But I need more information before working on designs and making mockups, and this needs to come both from an investigation into what is actually possible, and hearing back about what's actually needed and why.

Basically: We need to know what use cases we're actually trying to solve, address technical issues, possibly workaround limitations (as they _probably_still exist).

Again, I do greatly appreciate your effort (and we'll probably wind up using at least a big chunk of this code), but it needs more work on both ends (investigating, planning and designing upfront and then polishing at the end) before we merge in a change like this.

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Sorry for the slightly chaotic comments, I was reviewing the commits individually -- but so far they should be squashed and really be reviewed as a whole.

I don't know what @garrett and you talked about, and I'm aware that we are currently reorganizing the console a lot -- but given how important that seems for the customer, we don't need to let the perfect be the enemy of the good. The general intention and workflows seem clear enough, and the limitation to "listening to localhost only" avoids all the encryption/firewall/security issues.

We may well have to extend this in the future (listening to public addresses), but: (1) starting with that is already an improvement, and (2) it's actually good to encourage ssh tunelling over all this madness of encryption, TLS setup, and firewall configuration for VNC. This is really not how things should be done in production. So as long as we can get away with it, I'd like to stay with this localhost setup as long as possible, and rather work on virt-viewer / .vv download to work with ssh tunneling.

I still found a bunch of inconsistencies and bugs, but most of them are hopefully quick to fix. Thanks for driving this @mvollmer !

Comment on lines 67 to 69
vncAddress: this.state.vncAddress || "",
vncPort: this.state.vncPort || "",
vncPassword: this.state.vncPassword || "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of interest -- can these ever be null? Their state gets initialized as "", and you can't null-ify it via the PF widget. Neither the address nor the port can be 0. The password can be "0", but that's truthy. So overall this seems harmless.

return (
<Grid hasGutter md={6}>
<GridItem span={6}>
<FormGroup fieldId={`${idPrefix}-address`} label={_("VNC address")}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole dialog is about VNC, so I think we should leave out the "VNC " prefix from the labels. That shortens them and avoids potential inconsistent translations. Plus, both "Address" and "Port" are already translated, so we'll get them for free.

title={_("Edit VNC settings")}
footer={
<>
<Button isDisabled={this.state.saveDisabled} id={`${idPrefix}-save`} variant='primary' onClick={this.save}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveDisabled is initialized to false and never changed. This was probably meant to be used with some form validation? But all fields are optional, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the next "consoles: Improve VNC dialogs" commit there's actually some input validation, but it still doesn't disable the Save button AFAICS.

@@ -1089,3 +1089,29 @@ export async function domainAddTPM({ connectionName, vmName }) {
const args = ["virt-xml", "-c", `qemu:///${connectionName}`, "--add-device", "--tpm", "default", vmName];
return cockpit.spawn(args, { err: "message", superuser: connectionName === "system" ? "try" : null });
}

export function domainAttachVnc({ connectionName, vmName, vncAddress, vncPort, vncPassword }) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two are almost identical, and so are the dialogs. Merging the dialogs is a bit more work, but the API can easily be merged by adding either an edit boolean or a mode string that supports "add" or "edit" (and throw on any other value).

<>
<Grid hasGutter md={6}>
<GridItem span={6}>
<FormGroup fieldId={`${idPrefix}-address`} label={_("Listening address")}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Listening" is redundant here, a bit jargony, and makes the label unnecessarily wide (translations will only aggravate this). The address wasn't part of your recent round of screenshots but of course we need it.

vnc_info = _("not supported");
vnc_action = (
<Button variant="link" isInline onClick={add_vnc}>
{_("Add VNC")}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use the same string as above.

<p>
{
vm.state == "running"
? _("Shut down and restart the virtual machine to access the graphical console.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Shut down and // to trim the sentence.

{
vm.state == "running"
? _("Shut down and restart the virtual machine to access the graphical console.")
: _("Please start the virtual machine to access its console.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, let's make the sentences consistent. Use "its console" or "the graphical console" both here and in the line above.

Comment on lines +350 to +351
b.set_input_text("#add-vnc-port", "5000")
b.click("#add-vnc-add")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is type=text (see question above), this should also test a non-numeric value.

);
} else {
if (vnc.port == -1)
vnc_info = _("VNC, dynamic port");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a dynamic port it would be super helpful to actually show it, so that you know what to type into your virt-viewer. Can we get this from libvirt somehow? Is the port part of the downloaded .vv file?

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two additional shower thoughts.

</FormHelperText>
}
</FormGroup>
<FormGroup fieldId={`${idPrefix}-password`} label={_("Password")}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it actually make sense to offer a password for the "localhost only" use case? My gut feeling is "no", as that doesn't offer much protection in that use case. But @Shotaro-Kawaguchi should have the last word on that.


return (
<>
<FormGroup
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the dialog showing only the port, this could use an info alert/note that the server will only listen to localhost, and that you need to use ssh tunneling for remote access. If there's documentation about that somewhere, we should link to that. If not, let's not block on that, and people who don't know about that will then simply not use that feature -- they are no worse off than before.

@mvollmer
Copy link
Member Author

mvollmer commented Feb 5, 2025

Alright, in light of #1795 (comment), I propose to shelf this and replace it with a PR that only covers the "add VNC to a machine that is missing it" functionality.

@Shotaro-Kawaguchi, I hope that works for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants